package netService

import (
	"encoding/hex"
	"gitee.com/ling-bin/network/Receivers"
	"gitee.com/ling-bin/network/netInterface"
	"net"
	"sync"
	"sync/atomic"
	"time"
)

// Connection 连接结构体
type Connection struct {
	server           *Service                   //当前conn属于哪个server，在conn初始化的时候添加即可
	conn             net.Conn                   //当前连接的socket 套接字
	connId           uint64                     //当前连接的ID 也可以称作为SessionID，ID全局唯一
	session          interface{}                //标志(给业务层使用)
	isClosed         bool                       //当前连接的关闭状态 [ true:关闭，false:开 ]
	recPackCount     uint64                     //上行当前处理到的包数
	recTotalByteSize uint64                     //上行总大小(字节)
	repPackCount     uint64                     //下行总包个数
	repTotalByteSize uint64                     //下行总大小(字节)
	repPackErrCount  uint64                     //下发异常包个数
	incr             int64                      //供业务下发使用流水号
	heartTime        time.Time                  //连接最后一次接收数据时间(每包更新)
	sendTime         time.Time                  //连接最后一次发送数据时间(每包更新)
	startTime        time.Time                  //连接建立时间
	property         sync.Map                   //链接属性
	connLock         sync.RWMutex               //连接锁
	dynamicReceiver  *Receivers.DynamicReceiver //分包器
	addrConfig       *AddrConfig                //服务地址配置
}

// NewConnection 创建连接的方法
func NewConnection(server *Service, conn net.Conn, connId uint64, addrConfig *AddrConfig) *Connection {
	c := &Connection{
		server:     server,
		conn:       conn,
		connId:     connId,
		isClosed:   true,
		heartTime:  time.Now(),
		sendTime:   time.Now(),
		startTime:  time.Now(),
		property:   sync.Map{},
		addrConfig: addrConfig,
	}
	//将新创建的Conn添加到链接管理中
	c.server.GetConnMgr().Add(c)
	return c
}

// GetSession 获取session
func (c *Connection) GetSession() interface{} {
	return c.session
}

// SetSession 设置session
func (c *Connection) SetSession(session interface{}) {
	c.session = session
}

// GetNetConn 从当前连接获取原始的socket TCPConn
func (c *Connection) GetNetConn() interface{} {
	return c.conn
}

// GetNetwork 获取网络类型
func (c *Connection) GetNetwork() string {
	return c.server.config.Network
}

// GetConnId 获取当前连接ID
func (c *Connection) GetConnId() uint64 {
	return c.connId
}

// GetRemoteAddr 获取远程客户端地址信息
func (c *Connection) GetRemoteAddr() net.Addr {
	return c.conn.RemoteAddr()
}

// GetLocalAddr 获取本地地址信息
func (c *Connection) GetLocalAddr() net.Addr {
	return c.conn.LocalAddr()
}

// CallLogHandle 调用异常处理
func (c *Connection) CallLogHandle(level netInterface.ErrLevel, msgAry ...interface{}) {
	c.server.CallLogHandle(level, msgAry)
}

// GetIsClosed 获取的状态[脏读][ture:关闭状态，false:未关闭]
func (c *Connection) GetIsClosed() bool {
	return c.isClosed
}

// Incr 连接提供给业务作为流水号使用,循环累加,(val 为正数为递增，val为负数为递减,val为0则获取值)
func (c *Connection) Incr(val int64) int64 {
	return atomic.AddInt64(&c.incr, val)
}

// Start 启动连接，让当前连接开始工作[只会调用一次]
func (c *Connection) Start() {
	if !c.isClosed {
		return
	}
	c.isClosed = false
	//开启处理该链接读取到客户端数据之后的请求业务
	go c.startReader()
}

// Stop 停止连接，结束当前连接状态M
func (c *Connection) Stop() {
	if c.isClosed {
		return
	}

	c.connLock.Lock()
	if c.isClosed {
		c.connLock.Unlock()
		return
	}
	c.isClosed = true
	c.connLock.Unlock()

	// 关闭socket链接
	c.conn.Close()
}

// SendData 发送数据给远程的TCP客户端
// Data     下发数据
// CmdCode  指令标识[如: rep 普通回复, cmd 用户操作下发 。。]
func (c *Connection) SendData(data []byte, cmdCode string) error {
	return c.SendDataCall(data, cmdCode, nil, nil)
}

// SendDataCall 发送数据给远程的TCP客户端(带参数和回调)
// Data     下发数据
// Param    下发需要回调携带参数
// CmdCode  指令标识[如: rep 普通回复, cmd 用户操作下发 。。]
// CallFunc 下发后回调函数
func (c *Connection) SendDataCall(data []byte, cmdCode string, param interface{}, callFunc func(netInterface.IConnection, []byte, bool, string, interface{}, error)) error {
	if c.isClosed {
		return netInterface.ClosedErr
	}
	//更新发送时间
	c.sendTime = time.Now()

	//对象池获取
	reply := newReplyTask()
	reply.ConnId = c.connId
	reply.Data = data
	reply.CallFunc = callFunc
	reply.CmdCode = cmdCode
	reply.Param = param
	reply.RunReplyTask = c.runReplyTask

	if !c.server.config.OverflowDiscard {
		c.server.replyHandle.SendToTaskQueueWait(reply)
		return nil
	}
	err := c.server.replyHandle.SendToTaskQueue(reply)
	if err != nil {
		c.server.CallLogHandle(netInterface.Warn, "发送队列已满", err)
	}
	return err
}

// runReplyTask 运行回复任务
func (c *Connection) runReplyTask(replyTask *replyTask) {
	defer func() {
		if r := recover(); r != nil {
			c.CallLogHandle(netInterface.Error, "[tcp]运行回复任务异常:", r)
		}
	}()
	count := 0
	var tempDelay time.Duration
	sendData := replyTask.Data
RETRY: //遇到临时错误重试发送
	//设置发送超时时间
	err := c.conn.SetWriteDeadline(time.Now().Add(c.server.config.SendOutTime))
	if err != nil {
		c.replyNotice(replyTask, false, err)
		return
	}
	//发送数据[有异常信息或者发送成功数据和要发的数据不一致]
	if _, err := c.conn.Write(sendData); err != nil {
		if ne, ok := err.(net.Error); ok && ne.Temporary() {
			count++
			if count < c.server.config.SendRetryCount {
				if count == 0 {
					tempDelay = 1 * time.Microsecond
				} else {
					tempDelay *= 2
				}
				if max := 20 * time.Millisecond; tempDelay > max {
					tempDelay = max
				}
				time.Sleep(tempDelay)
				goto RETRY //临时错误重试
			}
		}
		c.replyNotice(replyTask, false, err)
		return
	}
	c.replyNotice(replyTask, true, nil)
}

// replyNotice 数据发送后通知
func (c *Connection) replyNotice(replyTask *replyTask, isOk bool, err error) {
	c.sendTime = time.Now()
	if replyTask.CallFunc != nil {
		replyTask.CallFunc(c, replyTask.Data, isOk, replyTask.CmdCode, replyTask.Param, err)
	}
	if isOk {
		c.repTotalByteSize += uint64(len(replyTask.Data))
		c.repPackCount++
	} else {
		c.repPackErrCount++
	}
	c.server.CallOnReply(c, replyTask.Data, isOk, replyTask.CmdCode, replyTask.Param, err)
}

// SetProperty 设置链接属性
func (c *Connection) SetProperty(key string, value interface{}) {
	c.property.Store(key, value)
}

// GetProperty 获取链接属性
func (c *Connection) GetProperty(key string) (interface{}, error) {
	if value, ok := c.property.Load(key); ok {
		return value, nil
	} else {
		return nil, netInterface.NotKey
	}
}

// RemoveProperty 移除链接属性
func (c *Connection) RemoveProperty(key string) {
	c.property.Delete(key)
}

// GetPropertyKeys 获取所有属性key
func (c *Connection) GetPropertyKeys() []string {
	propertyAry := make([]string, 0, 4)
	c.property.Range(func(key, value interface{}) bool {
		propertyAry = append(propertyAry, key.(string))
		return true
	})
	return propertyAry
}

// GetRecInfo 上行当前处理的包总数（处理前，1开始），总大小(字节)
func (c *Connection) GetRecInfo() (count, byteSize uint64) {
	return c.recPackCount, c.recTotalByteSize
}

// GetRepInfo 下行当前处理的包总数（处理后），总大小(字节)
func (c *Connection) GetRepInfo() (count, byteSize, errCount uint64) {
	return c.repPackCount, c.repTotalByteSize, c.repPackErrCount
}

// GetHeartTime 连接最后一次接收数据时间
func (c *Connection) GetHeartTime() time.Time {
	return c.heartTime
}

// GetSendTime  连接最后一次发送数据时间
func (c *Connection) GetSendTime() time.Time {
	return c.sendTime
}

// GetStartTime 连接建立时间
func (c *Connection) GetStartTime() time.Time {
	return c.startTime
}

// OnReceive 处理
func (c *Connection) OnReceive(data []byte) {
	//延期执行
	defer func() {
		if r := recover(); r != nil {
			c.CallLogHandle(netInterface.Error, "[tcp]数据包业务处理异常:", r)
		}
	}()
	c.recPackCount++
	c.heartTime = time.Now()

	var hData []byte
	if c.server.config.HDataCache {
		hData = data
	} else {
		hData = make([]byte, len(data))
		copy(hData, data)
	}
	c.server.CallOnReceive(c, hData)
}

// onCompleted 数据上传处理
func (c *Connection) onCompleted(data []byte) {
	//延期执行
	defer func() {
		if r := recover(); r != nil {
			c.CallLogHandle(netInterface.Error, "[tcp]数据分包处理异常:", r)
		}
	}()
	//连接关闭数据放弃处理
	if c.GetIsClosed() {
		return
	}
	//没有给分包则不分包
	if c.server.handleStrategy == nil {
		c.OnReceive(data)
		return
	}
	//tcp分包逻辑
	if c.dynamicReceiver == nil {
		strategyInfo := c.server.handleStrategy(c, data)
		if strategyInfo == nil || strategyInfo.Receivers == nil || len(strategyInfo.Receivers) == 0 {
			c.Stop()
			c.CallLogHandle(netInterface.Info, "第一包数据不能被分包识别:", hex.EncodeToString(data))
			return
		}
		c.SetProperty(HKey, strategyInfo.Key)
		if len(strategyInfo.ExtData) != 0 {
			for key, val := range strategyInfo.ExtData {
				c.SetProperty(key, val)
			}
		}
		c.dynamicReceiver = Receivers.NewDynamicReceiver(strategyInfo.Receivers, c.server.config.BufferSize, c.OnReceive, func(errStr string) {
			c.CallLogHandle(netInterface.Warn, "内部分包算法异常", errStr)
		})
	}

	//开启TLS配置后，不调用分包逻辑
	if c.addrConfig.IsTls {
		c.OnReceive(data)
	} else {
		//分包
		err := c.dynamicReceiver.Receiver(c, data)
		if err != nil {
			c.CallLogHandle(netInterface.Error, "分包算法内部异常:", err)
		}
	}
}

// startReader 处理conn读数据的Goroutine
func (c *Connection) startReader() {
	defer func() {
		//停止
		c.Stop()
		//发出关闭通知
		c.server.CallOnConnStop(c)
		//将链接从连接管理器中删除
		c.server.GetConnMgr().Remove(c)
		if c.dynamicReceiver != nil {
			c.dynamicReceiver.Recovery(c)
		}
		if r := recover(); r != nil {
			c.CallLogHandle(netInterface.Error, "[tcp]连接读取数据异常:", r)
		}
	}()
	//发出启动通知
	c.server.CallOnConnStart(c)

	data := c.server.bufferPool.Get()   //分配
	defer c.server.bufferPool.Put(data) //回收

	tempDelay := time.Millisecond * 0
	maxDelay := 1 * time.Second
	for {
		//设置读取超时时间，超时未读取到数据则关闭连接[心跳检测]
		if err := c.conn.SetReadDeadline(time.Now().Add(c.server.config.KeepTime)); err != nil {
			c.CallLogHandle(netInterface.Error, "[tcp]客户端设置读取时间异常： ", err)
			continue
		}
		count, err := c.conn.Read(data)
		if err != nil {
			ne, ok := err.(net.Error)
			if ok {
				if ne.Timeout() {
					c.CallLogHandle(netInterface.Error, "[tcp]客户端长时间未有数据上传： ", err)
					break
				} else if ne.Temporary() {
					if tempDelay == 0 {
						tempDelay = 5 * time.Microsecond
					} else {
						tempDelay *= 2
					}
					if tempDelay > maxDelay {
						tempDelay = maxDelay
					}
					c.CallLogHandle(netInterface.Error, "[tcp]客户端上传数据临时异常： ", err)
					time.Sleep(tempDelay)
					continue
				}
			}
			break
		}
		c.recTotalByteSize += uint64(count) //累加上行字节数
		c.onCompleted(data[:count])
		tempDelay = 0
	}
}
